#include "ClientRegistrar.hpp"
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include "JSONParser.hpp"
#include "Message.hpp"
#include "Types.hpp"
#include "ClientManager.hpp"
#include <cstdlib>
#include <sstream>
#include "Config.hpp"

using boost::asio::ip::tcp;

namespace netspy
{

void ClientRegistrar::setPort(const uint16_t &portNumber)
{
    portNumber_=portNumber;
}


void ClientRegistrar::registerSession(socket_ptr sock)
{
    try
    {
        char data[DATA_MAX_LENGTH];
        memset(data,0,DATA_MAX_LENGTH);
        boost::system::error_code error;
        sock->read_some(boost::asio::buffer(data), error);
        if (error == boost::asio::error::eof) return; // Connection closed cleanly by peer.
        else if (error) throw boost::system::system_error(error); // Some other error.		     
        Message message = JSONParser::unpack(data);
        
	if (message.command.compare(getCommandString(REGISTER)) == 0)
        {
            
	    if (Config::getInstance().isVerbose())
	    {
	    	std::cout << "Received request" << std:: endl;
            	std::cout << "\tCommand: " << message.command << std::endl;
	    	message.info.ip = sock->remote_endpoint().address().to_string();
            	std::cout << "\tHost info: " <<  "ip: " << message.info.ip << " port: " << message.info.port << std::endl;
            	std::cout << "\tHost info: " <<  "hash: " << message.info.hash << std:: endl;
            	std::string services;
            	for (ServiceList::const_iterator it=message.service_list.begin(); it != message.service_list.end(); ++it)
            	{
            	    services += (*it);
		    services += std::string(" ");
        	}
    
           	 std::cout << "\tHost info: " <<  "services_to_check: " << services << std::endl;
            }

            std::stringstream ss;
	    switch(ClientManager::getInstance().checkClient(message))
	    { 

		case ClientManager::NEW_CLIENT:
		{
			message.info.hash= ClientManager::getInstance().registerClient(message);
			ss<<"NEW CLIENT - hash - " << message.info.hash << std::endl;	
			message.info.status = 1;
			break;
		}
		case ClientManager::FORCE_NEW_CLIENT:
		{
			ss<<"FORCING CLIENT REGISTRATION"<<std::endl;
			message.info.status = 2;
			break;	
		}
		case ClientManager::REUSE_CLIENT_HASH:
		{
			ss<<"CLIENT ALREADY REGISTERED - REUSING"<<std::endl;
			message.info.status = 0;
			break;
		}

		case ClientManager::CLIENT_REJECTED:
		{
			message.info.status = -1;
			break;
		}
	    }
	 
	
	    if (Config::getInstance().isVerbose())
	    {
		std::cout<<"Decision: " << ss.str();
	    }
	
	}    
	else
	{
		message.command=getCommandString(UNKNOWN);
	}
	std::string answer=JSONParser::pack(message);
        boost::asio::write(*sock,boost::asio::buffer(answer.c_str(),answer.length()));
    }
  
    catch (std::exception& e)
    {
        std::cerr << "Exception in thread: " << e.what() << "\n";
    }

}


void ClientRegistrar::registerProcess()
{
    tcp::acceptor acceptor(registerService_, tcp::endpoint(tcp::v4(), portNumber_));
    for (;;)
    {
         socket_ptr sock(new tcp::socket(registerService_));
         acceptor.accept(*sock);
         boost::thread t(&ClientRegistrar::registerSession,this, sock);
    }

}

bool ClientRegistrar::run() 
{
    if (portNumber_ == 0) return false;
    if (isRunning_) return false;
 
    registerThread_ = boost::thread(&ClientRegistrar::registerProcess, this);  

    isRunning_ = true;
    return true;
	
}

}
